#ifndef XRPL_TEST_JTX_XCHAINBRIDGE_H_INCLUDED
#define XRPL_TEST_JTX_XCHAINBRIDGE_H_INCLUDED

#include <test/jtx/Account.h>
#include <test/jtx/amount.h>
#include <test/jtx/multisign.h>

#include <xrpl/json/json_value.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/XChainAttestations.h>

namespace ripple {
namespace test {
namespace jtx {

using JValueVec = std::vector<Json::Value>;

constexpr std::size_t UT_XCHAIN_DEFAULT_NUM_SIGNERS = 5;
constexpr std::size_t UT_XCHAIN_DEFAULT_QUORUM = 4;

Json::Value
bridge(
    Account const& lockingChainDoor,
    Issue const& lockingChainIssue,
    Account const& issuingChainDoor,
    Issue const& issuingChainIssue);

Json::Value
bridge_create(
    Account const& acc,
    Json::Value const& bridge,
    STAmount const& reward,
    std::optional<STAmount> const& minAccountCreate = std::nullopt);

Json::Value
bridge_modify(
    Account const& acc,
    Json::Value const& bridge,
    std::optional<STAmount> const& reward,
    std::optional<STAmount> const& minAccountCreate = std::nullopt);

Json::Value
xchain_create_claim_id(
    Account const& acc,
    Json::Value const& bridge,
    STAmount const& reward,
    Account const& otherChainSource);

Json::Value
xchain_commit(
    Account const& acc,
    Json::Value const& bridge,
    std::uint32_t claimID,
    AnyAmount const& amt,
    std::optional<Account> const& dst = std::nullopt);

Json::Value
xchain_claim(
    Account const& acc,
    Json::Value const& bridge,
    std::uint32_t claimID,
    AnyAmount const& amt,
    Account const& dst);

Json::Value
sidechain_xchain_account_create(
    Account const& acc,
    Json::Value const& bridge,
    Account const& dst,
    AnyAmount const& amt,
    AnyAmount const& xChainFee);

Json::Value
sidechain_xchain_account_claim(
    Account const& acc,
    Json::Value const& bridge,
    Account const& dst,
    AnyAmount const& amt);

Json::Value
claim_attestation(
    jtx::Account const& submittingAccount,
    Json::Value const& jvBridge,
    jtx::Account const& sendingAccount,
    jtx::AnyAmount const& sendingAmount,
    jtx::Account const& rewardAccount,
    bool wasLockingChainSend,
    std::uint64_t claimID,
    std::optional<jtx::Account> const& dst,
    jtx::signer const& signer);

Json::Value
create_account_attestation(
    jtx::Account const& submittingAccount,
    Json::Value const& jvBridge,
    jtx::Account const& sendingAccount,
    jtx::AnyAmount const& sendingAmount,
    jtx::AnyAmount const& rewardAmount,
    jtx::Account const& rewardAccount,
    bool wasLockingChainSend,
    std::uint64_t createCount,
    jtx::Account const& dst,
    jtx::signer const& signer);

JValueVec
claim_attestations(
    jtx::Account const& submittingAccount,
    Json::Value const& jvBridge,
    jtx::Account const& sendingAccount,
    jtx::AnyAmount const& sendingAmount,
    std::vector<jtx::Account> const& rewardAccounts,
    bool wasLockingChainSend,
    std::uint64_t claimID,
    std::optional<jtx::Account> const& dst,
    std::vector<jtx::signer> const& signers,
    std::size_t const numAtts = UT_XCHAIN_DEFAULT_QUORUM,
    std::size_t const fromIdx = 0);

JValueVec
create_account_attestations(
    jtx::Account const& submittingAccount,
    Json::Value const& jvBridge,
    jtx::Account const& sendingAccount,
    jtx::AnyAmount const& sendingAmount,
    jtx::AnyAmount const& rewardAmount,
    std::vector<jtx::Account> const& rewardAccounts,
    bool wasLockingChainSend,
    std::uint64_t createCount,
    jtx::Account const& dst,
    std::vector<jtx::signer> const& signers,
    std::size_t const numAtts = UT_XCHAIN_DEFAULT_QUORUM,
    std::size_t const fromIdx = 0);

struct XChainBridgeObjects
{
    // funded accounts
    Account const mcDoor;
    Account const mcAlice;
    Account const mcBob;
    Account const mcCarol;
    Account const mcGw;
    Account const scDoor;
    Account const scAlice;
    Account const scBob;
    Account const scCarol;
    Account const scGw;
    Account const scAttester;
    Account const scReward;

    // unfunded accounts
    Account const mcuDoor;
    Account const mcuAlice;
    Account const mcuBob;
    Account const mcuCarol;
    Account const mcuGw;
    Account const scuDoor;
    Account const scuAlice;
    Account const scuBob;
    Account const scuCarol;
    Account const scuGw;

    IOU const mcUSD;
    IOU const scUSD;

    Json::Value const jvXRPBridgeRPC;
    Json::Value jvb;   // standard xrp bridge def for tx
    Json::Value jvub;  // standard xrp bridge def for tx, unfunded accounts

    FeatureBitset const features;
    std::vector<signer> const signers;
    std::vector<signer> const alt_signers;
    std::vector<Account> const payee;
    std::vector<Account> const payees;
    std::uint32_t const quorum;

    STAmount const reward;                 // 1 xrp
    STAmount const split_reward_quorum;    // 250,000 drops
    STAmount const split_reward_everyone;  // 200,000 drops

    STAmount const tiny_reward;            // 37 drops
    STAmount const tiny_reward_split;      // 9 drops
    STAmount const tiny_reward_remainder;  // 1 drops

    STAmount const one_xrp;
    STAmount const xrp_dust;

    static constexpr int drop_per_xrp = 1000000;

    XChainBridgeObjects();

    void
    createMcBridgeObjects(Env& mcEnv);

    void
    createScBridgeObjects(Env& scEnv);

    void
    createBridgeObjects(Env& mcEnv, Env& scEnv);

    JValueVec
    att_create_acct_vec(
        std::uint64_t createCount,
        jtx::AnyAmount const& amt,
        jtx::Account const& dst,
        std::size_t const numAtts,
        std::size_t const fromIdx = 0)
    {
        return create_account_attestations(
            scAttester,
            jvb,
            mcCarol,
            amt,
            reward,
            payees,
            true,
            createCount,
            dst,
            signers,
            numAtts,
            fromIdx);
    }

    Json::Value
    create_bridge(
        Account const& acc,
        Json::Value const& bridge = Json::nullValue,
        STAmount const& _reward = XRP(1),
        std::optional<STAmount> const& minAccountCreate = std::nullopt)
    {
        return bridge_create(
            acc,
            bridge == Json::nullValue ? jvb : bridge,
            _reward,
            minAccountCreate);
    }
};

}  // namespace jtx
}  // namespace test
}  // namespace ripple

#endif
